package main

import (
	"flag"
	"fmt"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"

	"gitee.com/jason_elva8325/micro-quickstart/common"
	"gitee.com/jason_elva8325/micro-quickstart/config"
	"gitee.com/jason_elva8325/micro-quickstart/endpoint"
	"gitee.com/jason_elva8325/micro-quickstart/service"
	"gitee.com/jason_elva8325/micro-quickstart/transport"
	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/prometheus"
	"github.com/oklog/oklog/pkg/group"
	stdprometheus "github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
)

var (
	svcName        = flag.String("svc-name", "Say", "Service Name, use to unique service type")
	confFile       = flag.String("conf-file", "", "Host path of TOML format config file. If this param set, other params will be lose efficacy")
	listenHost     = flag.String("listen-host", "0.0.0.0", "TCP listen host")
	debugPort      = flag.String("debug-port", "9000", "Debug and metrics listen port")
	httpPort       = flag.String("http-port", "8081", "HTTP listen port")
	srdETCDServers = flag.String("srd-etcd-servers", "127.0.0.1:2379", "Comma-separated ETCD server addresses for Service Discovery & Registry. Example: '127.0.0.1:2379,127.0.0.1:13279'")
	srdETCDCaCert  = flag.String("srd-etcd-ca-cert", "", "ETCD client access root cert file path for Service Discovery & Registry")
	srdETCDCert    = flag.String("srd-etcd-cert", "", "ETCD client access cert file path for Service Discovery & Registry")
	srdETCDKey     = flag.String("srd-etcd-key", "", "ETCD client access cert's key file path for Service Discovery & Registry")
	srdPrefix      = flag.String("srd-prefix", "/svc/", "Service Discovery & Registry Prefix")
	rateLimit      = flag.Int("rate-limit", 0, "Times of service can be call per second. Less than or equal to zero means no limit")
	logger         log.Logger
)

func init() {
	// 获取主机名
	host, _ := os.Hostname()
	// 全局日志对象定义
	logger = log.NewJSONLogger(os.Stderr)
	logger = log.With(logger, "svc", *svcName)
	logger = log.With(logger, "host", host)
	logger = log.With(logger, "ts", log.DefaultTimestampUTC)
	logger = log.With(logger, "caller", log.DefaultCaller)
}

func main() {
	flag.Parse()

	// 生成服务配置对象
	conf := parseConfigure()

	// 启用服务注册
	registrar := common.ServiceRegistry(conf, common.HTTPTransport, logger)
	registrar.Register()

	// 启动服务量度
	var helloInts, goodbyInts metrics.Counter
	{
		// 业务量度
		helloInts = prometheus.NewCounterFrom(stdprometheus.CounterOpts{
			Namespace: "demo",
			Subsystem: "say_svc",
			Name:      "sayHello_integers_summed",
			Help:      "Total count of integers summed via the SayHello method.",
		}, []string{})

		goodbyInts = prometheus.NewCounterFrom(stdprometheus.CounterOpts{
			Namespace: "demo",
			Subsystem: "say_svc",
			Name:      "sayGoodby_integers_summed",
			Help:      "Total count of integers summed via the SayGoodby method.",
		}, []string{})
	}
	var duration metrics.Histogram
	{
		// 服务端点量度
		duration = prometheus.NewSummaryFrom(stdprometheus.SummaryOpts{
			Namespace: "demo",
			Subsystem: "say_svc",
			Name:      "request_duration_seconds",
			Help:      "Request duration in seconds.",
		}, []string{"method", "execute"})
	}
	http.DefaultServeMux.Handle("/metrics", promhttp.Handler())

	// 服务发布
	var (
		svc         = service.New(logger, helloInts, goodbyInts)
		end         = endpoint.New(svc, logger, duration, conf.Common.RateLimit)
		httpHandler = transport.NewHTTPHandler(end, logger)
	)

	var g group.Group
	{
		// The debug listener mounts the http.DefaultServeMux, and serves up
		// stuff like the Prometheus metrics route, the Go debug and profiling
		// routes, and so on.
		debugListener, err := net.Listen("tcp", conf.Common.ListenHost+":"+conf.Common.DebugPort)
		if err != nil {
			logger.Log("transport", "debug/HTTP", "during", "Listen", "err", err)
			os.Exit(1)
		}
		g.Add(func() error {
			logger.Log("transport", "debug/HTTP", "addr", conf.Common.ListenHost+":"+conf.Common.DebugPort)
			return http.Serve(debugListener, http.DefaultServeMux)
		}, func(error) {
			debugListener.Close()
		})
	}
	{
		// The HTTP listener mounts the Go kit HTTP handler we created.
		httpListener, err := net.Listen("tcp", conf.Common.ListenHost+":"+conf.HTTP.HTTPPort)
		if err != nil {
			logger.Log("transport", "HTTP", "during", "Listen", "err", err)
			os.Exit(1)
		}
		g.Add(func() error {
			logger.Log("transport", "HTTP", "addr", conf.Common.ListenHost+":"+conf.HTTP.HTTPPort)
			return http.Serve(httpListener, httpHandler)
		}, func(error) {
			httpListener.Close()
		})
	}
	{
		// This function just sits and waits for ctrl-C.
		cancelInterrupt := make(chan struct{})
		g.Add(func() error {
			c := make(chan os.Signal, 1)
			signal.Notify(c, syscall.SIGINT, syscall.SIGTERM)
			select {
			case sig := <-c:
				return fmt.Errorf("received signal %s", sig)
			case <-cancelInterrupt:
				registrar.Deregister() // 从 ETCD 中注销服务
				return nil
			}
		}, func(error) {
			close(cancelInterrupt)
		})
	}
	logger.Log("exit", g.Run())
}

// 生成服务配置对象
func parseConfigure() *config.ConfigFile {
	// 当配置文件设置不为空时，优先使用配置文件内容
	if strings.Trim(*confFile, " ") != "" {
		return config.ParseConfigFile(*confFile, logger)
	}
	return &config.ConfigFile{
		Common: &config.CommonConfig{
			SvcName:        *svcName,
			ListenHost:     *listenHost,
			DebugPort:      *debugPort,
			SRDETCDServers: *srdETCDServers,
			SRDETCDCaCert:  *srdETCDCaCert,
			SRDETCDCert:    *srdETCDCert,
			SRDETCDKey:     *srdETCDKey,
			SRDPrefix:      *srdPrefix,
			RateLimit:      *rateLimit,
		},
		HTTP: &config.HTTPConfig{
			HTTPPort: *httpPort,
		},
	}
}
